package com.zhangh.summer.admin.biz.auth.impl;

import com.zhangh.summer.admin.model.bo.AuthBO;
import com.zhangh.summer.admin.model.bo.DataBO;
import com.zhangh.summer.admin.model.po.*;
import com.zhangh.summer.admin.support.utils.BeanGenerator;
import com.zhangh.summer.admin.biz.auth.AuthBiz;
import com.zhangh.summer.admin.biz.menu.MenuBiz;
import com.zhangh.summer.admin.model.bo.MenuBO;
import com.zhangh.summer.admin.model.bo.OperationBO;
import com.zhangh.summer.admin.model.enums.RelationTypeEnum;

import com.zhangh.summer.admin.support.ds.common.MixedDS;
import com.zhangh.summer.admin.support.ds.common.pager.DataPager;
import com.zhangh.summer.admin.support.ds.common.pager.Pager;
import com.zhangh.summer.admin.support.exception.BizException;
import com.zhangh.summer.admin.support.id.IdGen;
import com.zhangh.summer.admin.support.session.SysSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: TODO
 * @author: zhanghui  zhanghlucky@163.com
 * @create: 2021-09-28 19:33
 **/
@Service
@Slf4j
public class AuthBizImpl implements AuthBiz {
    @Resource
    private MixedDS mixedDS;
    @Autowired
    private IdGen idGen;
    @Autowired
    private MenuBiz menuBiz;


    @Override
    public DataPager<AdAuthPO> getAuthDataPager(DataBO query, Pager pager) {
        AdAuthPOExample example=new AdAuthPOExample();
        AdAuthPOExample.Criteria c=example.createCriteria();
        if (StringUtils.isNotBlank(query.getName())){
            c.andAuthNameLike("%"+query.getName()+"%");
        }
        example.setOrderByClause("createTime desc");
        DataPager<AdAuthPO> dataPager=this.mixedDS.txDs().mapper().selectDataPager(example,pager);
        return dataPager;
    }

    @Override
    public AuthBO getAuthBO(long id) {
        AdAuthPO adAuthPO=this.mixedDS.txDs().mapper().selectByPrimaryKey(AdAuthPO.class,id);
        AuthBO authBO= BeanGenerator.copy(adAuthPO,AuthBO.class);
        List<MenuBO> menuList=this.menuBiz.getAuthMenuListForCheck(id);
        authBO.setMenuList(menuList);
        return authBO;
    }



    @Override
    public int saveAuthBO(AuthBO authBO) {
        AdAuthPO adAuthPO= BeanGenerator.copy(authBO,AdAuthPO.class);
        adAuthPO.setId(idGen.getID());
        adAuthPO.setCreateBy(SysSession.getUser().getLoginName());
        adAuthPO.setCreateTime(new Date());
        adAuthPO.setUpdateBy(SysSession.getUser().getLoginName());
        adAuthPO.setUpdateTime(new Date());
        int c1=this.mixedDS.txDs().mapper().insert(adAuthPO);
        log.info("保存权限 id {}",adAuthPO.getId());

        List<MenuBO> menuList=authBO.getMenuList();
        List<MenuBO> checkedMenuList=new ArrayList<>();
        List<OperationBO> checkedOperationList=new ArrayList<>();
        this.initChecked(menuList,checkedMenuList,checkedOperationList);

        List<Long> menuIDList=this.getMenuIDList(checkedMenuList);

        List<AdAuthRelationPO> insertList=new ArrayList<>();
        for (Long menuID : menuIDList) {
            AdAuthRelationPO relationPO=new AdAuthRelationPO();
            relationPO.setId(idGen.getID());
            relationPO.setAuthID(adAuthPO.getId());
            relationPO.setMenuID(menuID);
            relationPO.setOptID(0L);
            relationPO.setRelationType(RelationTypeEnum.MENU.getValue());
            insertList.add(relationPO);
        }
        if (insertList.size()>0){
            this.mixedDS.txDs().mapper().batchInsert(insertList,AdAuthRelationPO.class);
            log.info("保存权限菜单关系 size {}",insertList.size());
        }

        //权限操作关系
        //List<OperationBO> operationBOList=this.getOperationList(authBO.getMenuList());
        List<OperationBO> operationBOListForInsert=checkedOperationList.stream().filter(item->menuIDList.contains(item.getMenuID())).collect(Collectors.toList());
        List<AdAuthRelationPO> authOptInsertList=operationBOListForInsert.stream().map(item->{
            AdAuthRelationPO relationPO=new AdAuthRelationPO();
            relationPO.setId(idGen.getID());
            relationPO.setAuthID(adAuthPO.getId());
            relationPO.setMenuID(0L);
            relationPO.setOptID(item.getId());
            relationPO.setRelationType(RelationTypeEnum.OPT.getValue());
            return relationPO;
        }).collect(Collectors.toList());

        if (authOptInsertList.size()>0){
            this.mixedDS.txDs().mapper().batchInsert(authOptInsertList,AdAuthRelationPO.class);
            log.info("保存权限操作关系 size {}",authOptInsertList.size());
        }
        return c1;
    }

    @Override
    public int updateAuthBO(AuthBO authBO) {
        AdAuthPO adAuthPO= BeanGenerator.copy(authBO,AdAuthPO.class);
        adAuthPO.setUpdateBy(SysSession.getUser().getLoginName());
        adAuthPO.setUpdateTime(new Date());
        int c1=this.mixedDS.txDs().mapper().updateByPrimaryKey(adAuthPO);
        log.info("更新权限 id {}",adAuthPO.getId());

        AdAuthRelationPOExample relationPOExample=new AdAuthRelationPOExample();
        relationPOExample.createCriteria().andAuthIDEqualTo(adAuthPO.getId());
        List<AdAuthRelationPO> originalRelationPOList=this.mixedDS.txDs().mapper().selectByExample(relationPOExample);

        List<Long> originalMenuIDList=originalRelationPOList.stream().map(AdAuthRelationPO::getMenuID)
                .collect(Collectors.toList())
                    .stream().distinct().collect(Collectors.toList())
                    .stream().filter(item->item!=0).collect(Collectors.toList());

        List<Long> originalOptIDList=originalRelationPOList.stream().map(AdAuthRelationPO::getOptID)
                .collect(Collectors.toList())
                .stream().distinct().collect(Collectors.toList())
                .stream().filter(item->item!=0).collect(Collectors.toList());

        List<MenuBO> menuList=authBO.getMenuList();
        List<MenuBO> checkedMenuList=new ArrayList<>();
        List<OperationBO> checkedOperationList=new ArrayList<>();
        this.initChecked(menuList,checkedMenuList,checkedOperationList);

        List<Long> menuIDList=this.getMenuIDList(checkedMenuList);
        List<Long> menuIDInsertList=menuIDList.stream().filter(id->!originalMenuIDList.contains(id)).collect(Collectors.toList());
        List<Long> menuIDDeleteList=originalMenuIDList.stream().filter(id->!menuIDList.contains(id)).collect(Collectors.toList());

        //保存权限菜单关系
        List<AdAuthRelationPO> authMenuInsertList=new ArrayList<>();
        for (Long menuID : menuIDInsertList) {
            AdAuthRelationPO relationPO=new AdAuthRelationPO();
            relationPO.setId(idGen.getID());
            relationPO.setAuthID(adAuthPO.getId());
            relationPO.setMenuID(menuID);
            relationPO.setOptID(0L);
            relationPO.setRelationType(RelationTypeEnum.MENU.getValue());
            authMenuInsertList.add(relationPO);
        }
        if (authMenuInsertList.size()>0){
            this.mixedDS.txDs().mapper().batchInsert(authMenuInsertList,AdAuthRelationPO.class);
            log.info("保存权限菜单关系 size {}",authMenuInsertList.size());
        }
        //删除权限菜单关系
        for (Long menuID : menuIDDeleteList) {
            AdAuthRelationPOExample example=new AdAuthRelationPOExample();
            example.createCriteria().andMenuIDEqualTo(menuID).andAuthIDEqualTo(adAuthPO.getId());
            int c2=this.mixedDS.txDs().mapper().deleteByExample(example);
            log.info("删除权限菜单关系 menuID {}",menuID);
        }

        //权限操作关系
        //List<OperationBO> operationBOList=this.getOperationList(authBO.getMenuList());
        checkedOperationList=checkedOperationList.stream().filter(item->menuIDList.contains(item.getMenuID())).collect(Collectors.toList());

        List<Long> optIDList=checkedOperationList.stream().map(OperationBO::getId).collect(Collectors.toList());
        List<Long> optIDInsertList=optIDList.stream().filter(id->!originalOptIDList.contains(id)).collect(Collectors.toList());
        List<Long> optIDDeleteList=originalOptIDList.stream().filter(id->!optIDList.contains(id)).collect(Collectors.toList());

        //保存权限操作关系
        List<AdAuthRelationPO> authOptInsertList=optIDInsertList.stream().map(optID->{
            AdAuthRelationPO relationPO=new AdAuthRelationPO();
            relationPO.setId(idGen.getID());
            relationPO.setAuthID(adAuthPO.getId());
            relationPO.setMenuID(0L);
            relationPO.setOptID(optID);
            relationPO.setRelationType(RelationTypeEnum.OPT.getValue());
            return relationPO;
        }).collect(Collectors.toList());
        if (authOptInsertList.size()>0){
            this.mixedDS.txDs().mapper().batchInsert(authOptInsertList,AdAuthRelationPO.class);
            log.info("保存权限操作关系 size {}",authOptInsertList.size());
        }
        //删除权限操作关系
        for (Long optID : optIDDeleteList) {
            AdAuthRelationPOExample example=new AdAuthRelationPOExample();
            example.createCriteria().andOptIDEqualTo(optID).andAuthIDEqualTo(adAuthPO.getId());
            int c2=this.mixedDS.txDs().mapper().deleteByExample(example);
            log.info("删除权限操作关系 optID {}",optID);
        }
        return c1;
    }

    @Override
    @Transactional
    public int deleteAuth(Long id) {
        AdUserAuthPOExample example=new AdUserAuthPOExample();
        example.createCriteria().andAuthIDEqualTo(id);
        long c1=this.mixedDS.txDs().mapper().countByExample(example);
        if (c1>0){
            log.error("权限已经被使用，不能删除 id{}",id);
            throw BizException.exception("权限已经被使用，不能删除 id{0}",id);
        }
        int c2=this.mixedDS.txDs().mapper().deleteByPrimaryKey(AdAuthPO.class,id);
        log.info("删除权限 id {}",id);
        return c2;
    }

    @Override
    public List<AdAuthPO> getAuthList() {
        AdAuthPOExample example=new AdAuthPOExample();
        AdAuthPOExample.Criteria c=example.createCriteria();
        c.andIsEnableEqualTo(true);
        example.setOrderByClause("createTime desc");
        List<AdAuthPO> list=this.mixedDS.txDs().mapper().selectByExample(example);
        return list;

    }

    private List<Long> getMenuIDList(List<MenuBO> menuList){
        //权限关系
        //List<MenuBO> menuList=authBO.getMenuList();
        List<MenuBO> leafMenuList=menuList.stream().filter(item->(item.getChildren()==null||item.getChildren().size()==0))
                .collect(Collectors.toList());

        AdMenuPOExample menuPOExample=new AdMenuPOExample();
        List<AdMenuPO> originalMenuList=this.mixedDS.txDs().mapper().selectByExample(menuPOExample);

        List<Long> ids=new ArrayList<>();
        for (MenuBO menuBO : leafMenuList) {
            AdMenuPO adMenuPO= BeanGenerator.copy(menuBO,AdMenuPO.class);
            List<Long> list=this.getParents(adMenuPO,originalMenuList);
            ids.addAll(list);
        }
        //parentMenuIDList.stream().distinct();

        List<Long> menuIDList=leafMenuList.stream().map(MenuBO::getId).collect(Collectors.toList());
        ids.addAll(menuIDList);
        ids=ids.stream().distinct().collect(Collectors.toList());
        //menuIDList.addAll(parentMenuIDList);
        //return menuIDList;
        return ids;
    }
    private void initChecked(List<MenuBO> menuList,List<MenuBO> checkedMenuList,List<OperationBO> checkedOperationList){
        for (MenuBO menuBO : menuList) {
            if (menuBO.isChecked()){
                checkedMenuList.add(menuBO);
            }
            for (OperationBO operationBO : menuBO.getOperations()) {
                if (operationBO.isChecked()){
                    checkedOperationList.add(operationBO);
                }
            }
            if (null!=menuBO.getChildren()&&menuBO.getChildren().size()>0){
                this.initChecked(menuBO.getChildren(),checkedMenuList,checkedOperationList);
            }
        }


    }

    private List<Long> getParents(AdMenuPO menuPO,List<AdMenuPO> allMenuPOList){
        List<Long> list=new ArrayList<>();
        for (AdMenuPO adMenuPO : allMenuPOList) {
            if (menuPO.getPid()!=0&&menuPO.getPid().equals(adMenuPO.getId())){
                list.add(adMenuPO.getId());
                List<Long> childList=getParents(adMenuPO,allMenuPOList);
                list.addAll(childList);
            }
        }
        return list;
    }

}
